#ifndef __UTILS_H
#define __UTILS_H

#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <syscall.h>
#include <errno.h>
#include <signal.h>

// pretty colors for output           
// These don't work with UPC for some reason. Pooh.
#define _RED_ "\x1b[31m"
#define _GREEN_ "\x1b[32m"
#define _BROWN_ "\x1B[33m"
#define _BLUE_ "\x1b[34m"
#define _MAGENTA_ "\x1b[35m"
#define _CYAN_ "\x1b[36m"
#define _LIGHTGRAY_ "\x1B[37m"
#define _DARKGRAY_ "\x1B[1m\x1B[0m"
#define _LIGHTRED_ "\x1B[1m\x1B[31m"
#define _LIGHTGREEN_ "\x1B[1m\x1B[32m"
#define _YELLOW_ "\x1B[1m\x1B[33m"
#define _LIGHTBLUE_ "\x1B[1m\x1B[34m"
#define _LIGHTMAGENTA_ "\x1B[1m\x1B[35m"
#define _LIGHTCYAN_ "\x1B[1m\x1B[36m"
#define _END_ "\x1b[0m"

enum dbg_flag_t {DSTARTUP, DMIGRATE, DTASKS, DBALANCE, DSUMMARY, DNUMA, DCORES, DEND_FLAGS};
extern const char* dbg_names[];
extern const char* dbg_colors[];
extern enum dbg_flag_t dbg_flags;
extern pid_t _exec_pid;

#define DBG(FLAG, fmt, ...)                                             \
	do {																\
		if ((1 << FLAG) & dbg_flags) {									\
			printf("%s%s [%.3f] " fmt _END_ "\n", dbg_colors[FLAG], dbg_names[FLAG], \
				   get_current_time(), __VA_ARGS__);}					\
	} while (0);
#define NOTICE(fmt, ...)												\
	printf(_LIGHTRED_ "%s [%.3f] " fmt _END_ "\n", "NTCE", get_current_time(), __VA_ARGS__);
#define WARN(fmt, ...)													\
	printf(_RED_ "%ld: WARNING: " fmt _END_ "\n", syscall(__NR_gettid), __VA_ARGS__); 
#define FAIL(fmt, ...)													\
	do {																\
		printf(_RED_ "%ld: FAIL: " fmt _END_ "\n", syscall(__NR_gettid), __VA_ARGS__); \
		if (_exec_pid > 0) kill(_exec_pid, SIGKILL);					\
		exit(EXIT_FAILURE);												\
	} while(0);

#define CHECK_CALL(call)											\
	do {															\
		if ((call) == -1) FAIL("%s: %s", #call, strerror(errno));	\
	} while (0);
#define CHECK_CALL_RET(call, ret)										\
	do {																\
		if ((ret = (call)) == -1) FAIL("%s: %s (%d)", #call, strerror(errno), errno); \
	} while (0);
#define CHECK_PTHREAD_CALL(call)										\
	do {                                                                \
		if ((errno = (call) != 0)) FAIL("%s: %s", #call, strerror(errno)); \
	} while (0);

#define MX_LOCK(mx) CHECK_PTHREAD_CALL(pthread_mutex_lock(mx))
#define MX_UNLOCK(mx) CHECK_PTHREAD_CALL(pthread_mutex_unlock(mx))

#define NOT_SET -1
#define IS_SET(A) ((A) != NOT_SET)

#define IS_EMPTY(S) (S[0] == '\0')

void check_sched_compat_yield();
int get_cid_from_procfs(pid_t ppid, pid_t pid);
int get_rnd(int range, unsigned int *seed);
double init_starting_time();
double get_current_time();
void get_process_times(double *parent_time, double *children_time);
void set_process_affinity();
void mx_init(pthread_mutex_t* mx, int attr);
int pin_thread(pid_t pid, int cpu);
int pin_thread_multi(pid_t pid, int* cpus, int num_cpus);
void check_is_pinned(pid_t ppid, pid_t pid, int cid);
int is_task_running(pid_t pid);
long long get_cpu_idle_ticks(int cid, int fast);
double get_clock_res();
int get_task_stats_from_proc(pid_t pid, pid_t tid, 
							 double *utime, double *stime, double *rtime);
int get_hyperthreads_per_core();

#endif
